package com.example.zhu29.bledemo.util;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.util.Log;

import com.example.zhu29.bledemo.Contast;
import com.example.zhu29.bledemo.interFace.IBleConnectStatusListener;
import com.example.zhu29.bledemo.interFace.IBleIndicateListener;
import com.example.zhu29.bledemo.interFace.IBleScanListener;
import com.example.zhu29.bledemo.interFace.IBleStateListener;
import com.example.zhu29.bledemo.interFace.IBleWriteListener;
import com.inuker.bluetooth.library.BluetoothClient;
import com.inuker.bluetooth.library.connect.listener.BleConnectStatusListener;
import com.inuker.bluetooth.library.connect.listener.BluetoothStateListener;
import com.inuker.bluetooth.library.connect.options.BleConnectOptions;
import com.inuker.bluetooth.library.connect.response.BleConnectResponse;
import com.inuker.bluetooth.library.connect.response.BleNotifyResponse;
import com.inuker.bluetooth.library.connect.response.BleReadResponse;
import com.inuker.bluetooth.library.connect.response.BleUnnotifyResponse;
import com.inuker.bluetooth.library.connect.response.BleWriteResponse;
import com.inuker.bluetooth.library.model.BleGattProfile;
import com.inuker.bluetooth.library.receiver.listener.BluetoothBondListener;
import com.inuker.bluetooth.library.search.SearchRequest;
import com.inuker.bluetooth.library.search.SearchResult;
import com.inuker.bluetooth.library.search.response.SearchResponse;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;

import static com.inuker.bluetooth.library.Constants.REQUEST_FAILED;
import static com.inuker.bluetooth.library.Constants.REQUEST_NOTIFY;
import static com.inuker.bluetooth.library.Constants.REQUEST_READ;
import static com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS;
import static com.inuker.bluetooth.library.Constants.REQUEST_WRITE;
import static com.inuker.bluetooth.library.Constants.STATUS_CONNECTED;
import static com.inuker.bluetooth.library.Constants.STATUS_DISCONNECTED;

/**
   * Author: zhudewei
   * Time: 2018/1/17
   * code is never no bug
   */

public class ZBleManager {
    private IBleConnectStatusListener mConnectListener;
    private IBleScanListener mIBleScanListener;
    private IBleIndicateListener mIBleIndicateListener;
    private IBleWriteListener mIBleWriteListener;
    private IBleStateListener mIBleStateListener;
    private boolean isConnected = false;
    private BluetoothClient mClient;
    private List<SearchResult> mList;
    private Disposable mDisposable;
    private BluetoothStateListener mBluetoothStateListener;

    private static class Holder {
        private static final ZBleManager INSTANCE = new ZBleManager();
    }

    private ZBleManager() {
    }

    public static ZBleManager getInstance() {
        return Holder.INSTANCE;
    }

    public void addDeviceFounded(SearchResult result) {
        mList.add(result);
    }

    public void initBle(Context context) {
        mList = new ArrayList<>();
        mClient = getBleManager(context);
        //Search listener
        mSearchResponse = new SearchResponse() {
            @Override
            public void onSearchStarted() {
                if (mIBleScanListener != null) {
                    mIBleScanListener.onScanStarted();
                }
            }

            @Override
            public void onDeviceFounded(SearchResult device) {
                if (null == mList) return;
                Log.e("zdx", "----" + device.getName() + "---mList---" + mList.size());
                if (!mList.contains(device)) {
                    mList.add(device);
//                    if (mList.size()==1)return;
                    if (mIBleScanListener != null) mIBleScanListener.onDeviceFounded(device);
                }
            }

            @Override
            public void onSearchStopped() {
                if (mIBleScanListener != null) mIBleScanListener.onScanStop();
            }

            @Override
            public void onSearchCanceled() {
                if (mIBleScanListener != null) mIBleScanListener.onScanCanceled();
            }
        };

        //Paired monitoring
        mBleConnectStatusListener = new BleConnectStatusListener() {
            @Override
            public void onConnectStatusChanged(String mac, int status) {
                if (status == STATUS_CONNECTED) {
                    isConnected = true;
                } else if (status == STATUS_DISCONNECTED) {
                    isConnected = false;
                    if (null != mConnectListener) mConnectListener.onDisConnected(mac);
                }
            }
        };
        mBluetoothBondListener = new BluetoothBondListener() {
            @Override
            public void onBondStateChanged(String mac, int bondState) {

            }
        };
        mClient.registerBluetoothBondListener(mBluetoothBondListener);

        //Monitor Bluetooth on status
        mBluetoothStateListener = new BluetoothStateListener() {
            @Override
            public void onBluetoothStateChanged(boolean openOrClosed) {
                if (null != mIBleStateListener && openOrClosed) {
                    mIBleStateListener.onBleOpen();
                }
            }
        };
    }

    private BluetoothClient getBleManager(Context context) {
        return new BluetoothClient(context);
    }

    public void openBluetooth(IBleStateListener listener) {
        if (null != mClient) {
            this.mIBleStateListener = listener;
            mClient.openBluetooth();
            mClient.registerBluetoothStateListener(mBluetoothStateListener);
        }
    }

    public void closeBluetooth() {
        if (null != mClient) mClient.closeBluetooth();
    }

    public boolean isBlueOpen() {
        return mClient.isBluetoothOpened();
    }

    public boolean isSupportBle() {
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        return null != adapter;
    }

    public boolean isConnected() {
        return isConnected;
    }

    public int getConnectStatus(String mac) {
        int code = mClient.getConnectStatus(mac);
        return code;
    }

    public void cancelScan() {
        if (null != mClient) mClient.stopSearch();
    }

    public void disConnectDevice(String mac) {
        if (null != mClient) mClient.disconnect(mac);
    }

    public void destroy(String mac) {
        // Constants.REQUEST_READ，All read requests
        // Constants.REQUEST_WRITE，All write requests
        // Constants.REQUEST_NOTIFY，All notification related requests
        // Constants.REQUEST_RSSI，All requests for read signal strength
        if (null != mClient) {
            mClient.stopSearch();
//            mClient.disconnect(mac);
            mClient.clearRequest(mac, REQUEST_READ);
            mClient.clearRequest(mac, REQUEST_WRITE);
            mClient.clearRequest(mac, REQUEST_NOTIFY);
            mClient.refreshCache(mac);
            mClient.unregisterConnectStatusListener(mac, mBleConnectStatusListener);
            mClient.unregisterBluetoothBondListener(mBluetoothBondListener);
            mClient.unregisterBluetoothStateListener(mBluetoothStateListener);
        }
        unIndicate(mac, Contast.GATT_SERVICE_PRIMARY, Contast.CHARACTERISTIC_READABLE);
        if (null != mBleConnectStatusListener) mBleConnectStatusListener = null;
        if (null != mBluetoothBondListener) mBluetoothBondListener = null;
        if (mIBleScanListener != null) mIBleScanListener = null;
        if (mConnectListener != null) mConnectListener = null;
        if (mIBleIndicateListener != null) mIBleIndicateListener = null;
        if (mIBleWriteListener != null) mIBleWriteListener = null;
        if (mSearchResponse != null) mSearchResponse = null;
        mList = null;
        isConnected = false;
        if (null != mDisposable) mDisposable.dispose();
    }

    public void indicate(String MAC, String serviceUUID, String characterUUID, IBleIndicateListener callback) {
        if (null == mClient) return;
        this.mIBleIndicateListener = callback;
        mClient.notify(MAC, UUID.fromString(serviceUUID), UUID.fromString(characterUUID), new BleNotifyResponse() {
            @Override
            public void onNotify(UUID service, UUID character, byte[] value) {
                if (null != mIBleIndicateListener)
                    mIBleIndicateListener.onCharacteristicChanged(value);
            }

            @Override
            public void onResponse(int code) {
                if (code == REQUEST_SUCCESS) {
                    if (null != mIBleIndicateListener) mIBleIndicateListener.onIndicateSuccess();
                } else if (REQUEST_FAILED == code) {
                    if (null != mIBleIndicateListener)
                        mIBleIndicateListener.onIndicateFailure(code);
                }
            }
        });
    }

    public void unIndicate(String MAC, String serviceUUID, String characterUUID) {
        if (null == mClient) return;
        mClient.unnotify(MAC, UUID.fromString(serviceUUID), UUID.fromString(characterUUID), new BleUnnotifyResponse() {
            @Override
            public void onResponse(int code) {

            }
        });
    }


    public void write(String MAC, String uuid_service, String uuid_write, final byte[] data, IBleWriteListener callback) {
        if (null == mClient) return;
        this.mIBleWriteListener = callback;
        mClient.write(MAC, UUID.fromString(uuid_service), UUID.fromString(uuid_write), data, new BleWriteResponse() {
            @Override
            public void onResponse(int code) {
                if (code == REQUEST_SUCCESS) {
                    if (null != mIBleWriteListener) mIBleWriteListener.onWriteSuccess();
                } else {
                    if (null != mIBleWriteListener) mIBleWriteListener.onWriteFailure(code);
                }
            }
        });
    }

    public void read(String MAC, String uuid_service, String uuid_write) {
        if (null == mClient) return;
        mClient.read(MAC, UUID.fromString(uuid_service), UUID.fromString(uuid_write), new BleReadResponse() {
            @Override
            public void onResponse(int code, byte[] data) {
            }
        });
    }

    private BleConnectStatusListener mBleConnectStatusListener;
    private BluetoothBondListener mBluetoothBondListener;


    @SuppressLint("CheckResult")
    public void connect(final String mac, IBleConnectStatusListener listener) {
        if (null == mClient) return;
        this.mConnectListener = listener;
        mDisposable = Flowable.timer(500, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) throws Exception {
                        final BleConnectOptions options = new BleConnectOptions.Builder()
                                .setConnectRetry(3)   // Retry the connection 3 times if it fails
                                .setConnectTimeout(5000)   // Connect Timeout 5s
                                .setServiceDiscoverRetry(3)  // Retry service discovery 3 times if it fails
                                .setServiceDiscoverTimeout(5000)  // Discovery of service timeout 5s
                                .build();
                        if (mConnectListener != null) {
                            mConnectListener.onStartConnect();
                        }
                        mClient.connect(mac, options, new BleConnectResponse() {
                            @Override
                            public void onResponse(int code, BleGattProfile data) {
                                if (code == REQUEST_SUCCESS) {
                                    if (null != mConnectListener)
                                        if (null != mConnectListener)
                                            mConnectListener.onConnectSuccess(code, data, mac);
                                } else if (code == REQUEST_FAILED) {
                                    if (null != mConnectListener) mConnectListener.onConnectFail();
                                }
                            }
                        });
                    }
                });
        mClient.registerConnectStatusListener(mac, mBleConnectStatusListener);
    }

    private SearchResponse mSearchResponse;

    public void scan(IBleScanListener listener) {
        if (null == mClient) return;
        this.mIBleScanListener = listener;
        SearchRequest request = new SearchRequest.Builder()
                .searchBluetoothLeDevice(3000, 3)   // Start by scanning your BLE devices 3 times， every time 3s
                .searchBluetoothClassicDevice(5000) // and scanning the traditional bluetooth devices 5s
                .searchBluetoothLeDevice(2000)      //and scanning BLE devices 2s
                .build();
        if (null != mSearchResponse) mClient.search(request, mSearchResponse);
    }
}
